package com.zjf.fupload.ftp;

import it.sauronsoftware.ftp4j.FTPAbortedException;
import it.sauronsoftware.ftp4j.FTPClient;
import it.sauronsoftware.ftp4j.FTPDataTransferException;
import it.sauronsoftware.ftp4j.FTPDataTransferListener;
import it.sauronsoftware.ftp4j.FTPException;
import it.sauronsoftware.ftp4j.FTPFile;
import it.sauronsoftware.ftp4j.FTPIllegalReplyException;
import it.sauronsoftware.ftp4j.FTPListParseException;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FilenameFilter;
import java.io.IOException;

import com.zjf.fupload.ftp.UploadException.ErrorType;
import com.zjf.fupload.util.FileUtil;

/**
 * @author zjf
 */
public class FTPUploader {

	public static final int DEFAULT_PORT = 21;
	
	/**
	 * 服务器
	 */
	private String server;
	private int port = DEFAULT_PORT;
	/**
	 * 远程路径
	 */
	private String path = "/";
	
	private String username;
	private String password;
	
	/**
	 * 是否被取消
	 */
	private volatile boolean isCanceled = false;
	
	private UploadListener listener;
	
	/**
	 * 字符编码，默认为中文window下的编码，此处可以防止中文文件名乱码。
	 * TODO: 应该获取系统的编码
	 */
	private String charset = "GBK";
	
	public FTPUploader(String server, int port, String path) {
		this.server = server;
		this.port = port;
		this.path = path;
	}
	
	public FTPUploader(String server, String path) {
		this.server = server;
		this.path = path;
	}
	
	public void upload(File[] files) throws UploadException {
		assert files != null;
		listener.started();
		FTPClient client = new FTPClient();
		try {
			client.setType(FTPClient.TYPE_BINARY);
			client.setCharset(charset);
	    	client.connect(server, port);
	    	client.login(username, password);
	    	client.changeDirectory(path);
	    	
	    	final long totalBytes = FileUtil.getSize(files);
	    	
			uploadInterval(client, files, totalBytes);
		} catch (IllegalStateException e) {
			throw new UploadException("FTP非法使用", e);
		} catch (IOException e) {
			throw new UploadException(ErrorType.io, "网络异常", e);
		} catch (FTPIllegalReplyException e) {
			throw new UploadException("FTP服务器拒绝", e);
		} catch (FTPException e) {
			throw new UploadException("异常", e);
		} catch (FTPDataTransferException e) {
			throw new UploadException("FTP数据传输异常", e);
		} catch (FTPAbortedException e) {
			throw new UploadException("FTP数据传输被中断", e);
		} catch (FTPListParseException e) {
			throw new UploadException("FTP获取文件列表错误", e);
		} finally {
			listener.completed();
			try {
				client.disconnect(true);
			} catch(Exception e) {
				//ignore
			}
		}
	}
	
	private void uploadInterval(FTPClient client, File[] files, final long totalBytes) 
	throws IllegalStateException, FileNotFoundException, IOException, 
		FTPIllegalReplyException, FTPException, 
		FTPDataTransferException, FTPAbortedException, FTPListParseException  {
		
		//
		if(files == null || files.length <= 0) {
			return;
		}
		
		if(isCanceled) {
			listener.canceled();
			return;
		}
			
		for(final File file : files) {
			
			if(isCanceled) {
				listener.canceled();
				break;
			}
			
			if(file.isFile()) {
				client.upload(file, new FTPDataTransferListener() {
					@Override
					public void transferred(int length) {
						listener.transferred(totalBytes, length);
					}
					@Override
					public void started() {
						listener.fileUploadStart(file);
					}
					
					@Override
					public void failed() {
						listener.fileUploadFail(file);
					}
					@Override
					public void completed() {
						listener.fileUploadCompleted(file);
					}
					@Override
					public void aborted() {
						listener.fileUploadAborted(file);
					}
				});
			} else if(file.isDirectory()) {
				uploadDir(client, file, totalBytes);
			}
		}
		
	}
	
	private void uploadDir(FTPClient client, File dir, final long totalBytes) throws IllegalStateException, 
		IOException, FTPIllegalReplyException, FTPException, FTPDataTransferException, 
		FTPAbortedException, FTPListParseException {
		if(!hasDir(client, dir.getName())) {
			client.createDirectory(dir.getName());
		}
		client.changeDirectory(dir.getName());
		File[] children = dir.listFiles(new FilenameFilter() {
			@Override
			public boolean accept(File dir, String name) {
				return !name.startsWith(".");
			}
		});
		uploadInterval(client, children, totalBytes);
		client.changeDirectoryUp();
	}
	
	private boolean hasDir(FTPClient client, String dir) throws IllegalStateException, 
		IOException, FTPIllegalReplyException, FTPException, FTPDataTransferException, 
		FTPAbortedException, FTPListParseException {

		FTPFile[] ftpFiles = client.list();
		for(FTPFile ff : ftpFiles) {
			if(ff.getType() == FTPFile.TYPE_DIRECTORY && ff.getName().equals(dir)) {
				return true;
			}
		}
		
		return false;
		
	}
	
	//setter & getter

	public String getServer() {
		return server;
	}

	public void setServer(String server) {
		this.server = server;
	}

	public int getPort() {
		return port;
	}

	public void setPort(int port) {
		this.port = port;
	}

	public String getPath() {
		return path;
	}

	public void setPath(String path) {
		this.path = path;
	}

	public String getUsername() {
		return username;
	}

	public void setUsername(String username) {
		this.username = username;
	}

	public String getPassword() {
		return password;
	}

	public void setPassword(String password) {
		this.password = password;
	}

	public boolean isCanceled() {
		return isCanceled;
	}

	public void setCanceled(boolean isCanceled) {
		this.isCanceled = isCanceled;
	}

	public UploadListener getListener() {
		return listener;
	}

	public void setListener(UploadListener listener) {
		this.listener = listener;
	}

	public static int getDefaultPort() {
		return DEFAULT_PORT;
	}
	
	
	
}
